package com.jyw.business.ins.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.jyw.business.ins.domain.InsPlanExecutionTime;
import com.jyw.business.ins.domain.model.CreateTaskDays;
import com.jyw.business.ins.domain.model.CreateTaskTimeRage;
import com.jyw.business.ins.domain.model.CreateTaskWeek;
import lombok.experimental.UtilityClass;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Created by ZhangCheng on 2023/12/25 （巡检任务工具类）
 */
@UtilityClass
public class InspectionTaskUtil {

    /**
     * 默认周天数
     */
    public static final String WEEKLY_DAYS = "1,2,3,4,5,6,7";

    /**
     * 每季度天
     */
    public static final int QUARTER_DAY = 90;

    /**
     * 每半年天
     */
    public static final int ANNUAL_DAY = 180;

    /**
     * 每年天
     */
    public static final int YEAR_DAY = 365;

    /**
     * 判断日期是否存在区间
     *
     * @param startDate
     * @param endDate
     * @param datumDate
     * @return
     */
    public boolean filterDate(LocalDate startDate, LocalDate endDate, LocalDate datumDate) {
        boolean bool = ((datumDate.isAfter(startDate) || datumDate.isEqual(startDate)) && (datumDate.isBefore(endDate) || datumDate.isEqual(endDate)));
        return bool;
    }

    /**
     * 获取每月天
     *
     * @return
     */
    public List<String> getMonthlyDays(LocalDate localDate) {
        List<String> filterMonthly = new ArrayList<>();
        int dayOfMonth = localDate.lengthOfMonth();
        for (int i = 0; i < dayOfMonth; i++) {
            filterMonthly.add(String.valueOf(i + 1));
        }
        return filterMonthly;
    }

    /**
     * 按类型获取所有天数
     *
     * @return
     */
    public List<String> getAllDays(int day) {
        List<String> allDays = new ArrayList<>();
        for (int i = 0; i < day; i++) {
            allDays.add(String.valueOf(i + 1));
        }
        return allDays;
    }


    /**
     * 获取每天执行时间
     *
     * @param timeList
     * @param currentTime
     * @param inspectionNum
     * @return
     */
    public List<CreateTaskTimeRage> getDayTaskTime(List<InsPlanExecutionTime> timeList, LocalDateTime currentTime, Integer inspectionNum) {
        if (CollUtil.isNotEmpty(timeList)) {
            List<CreateTaskTimeRage> timeRages = new ArrayList<>();
            // 拼接固定时间
            timeList.forEach(execution -> {
                if (execution.getStartTime() != null && execution.getEndTime() != null) {
                    CreateTaskTimeRage timeRage = CreateTaskTimeRage.builder()
                            .startTime(currentTime.with(execution.getStartTime()))
                            .endTime(currentTime.with(execution.getEndTime()))
                            .build();
                    timeRages.add(timeRage);
                }
            });
            return timeRages;
        } else {
            // 获取每日执行时间 根据执行次数
            List<CreateTaskTimeRage> timeRages = getDayTimeRage(currentTime, inspectionNum);
            return timeRages;
        }
    }

    /**
     * 获取每周执行时间
     *
     * @param taskWeeks
     * @param timeList
     * @param inspectionNum
     * @param monday
     * @return
     */
    public List<CreateTaskWeek> getWeekTaskTime(List<CreateTaskWeek> taskWeeks, List<InsPlanExecutionTime> timeList, Integer inspectionNum, LocalDate monday) {
        List<CreateTaskWeek> filterWeeks = taskWeeks.stream().filter(a -> a.getNum() > 0).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(filterWeeks)) {
            int count = filterWeeks.size();
            boolean bool = false;
            // 判断执行时间是否为空 是否和巡检次数相等
            if (CollUtil.isNotEmpty(timeList)) {
                bool = timeList.size() == inspectionNum ? true : false;
            }
            if (bool) {
                int num = inspectionNum / count;
                int over = inspectionNum % count;
                for (int i = 0; i < count; i++) {
                    CreateTaskWeek currentWeek = filterWeeks.get(i);
                    int week = currentWeek.getWeek();
                    LocalDateTime weekDate = monday.plusDays((week - 1)).atStartOfDay();
                    List<CreateTaskTimeRage> timeRages = new ArrayList<>();
                    for (int j = 0; j < num; j++) {
                        int index = (j * count) + i;
                        InsPlanExecutionTime execution = timeList.get(index);
                        CreateTaskTimeRage timeRage = CreateTaskTimeRage.builder()
                                .startTime(weekDate.with(execution.getStartTime()))
                                .endTime(weekDate.with(execution.getEndTime()))
                                .build();
                        timeRages.add(timeRage);
                    }
                    currentWeek.setTimeRages(timeRages);
                }
                for (int i = 0; i < over; i++) {
                    if (i < filterWeeks.size()) {
                        int index = (num * count) + i;
                        InsPlanExecutionTime execution = timeList.get(index);
                        CreateTaskWeek overWeek = filterWeeks.get(i);
                        int week = overWeek.getWeek();
                        LocalDateTime weekDate = monday.plusDays((week - 1)).atStartOfDay();
                        CreateTaskTimeRage timeRage = CreateTaskTimeRage.builder()
                                .startTime(weekDate.with(execution.getStartTime()))
                                .endTime(weekDate.with(execution.getEndTime()))
                                .build();
                        if (overWeek.getTimeRages() != null) {
                            overWeek.getTimeRages().add(timeRage);
                        } else {
                            List<CreateTaskTimeRage> timeRages = new ArrayList<>();
                            timeRages.add(timeRage);
                            overWeek.setTimeRages(timeRages);
                        }
                    }
                }
            } else {
                for (int i = 0; i < count; i++) {
                    CreateTaskWeek currentWeek = filterWeeks.get(i);
                    int week = currentWeek.getWeek();
                    int num = currentWeek.getNum();
                    LocalDateTime currentTime = monday.plusDays((week - 1)).atStartOfDay();
                    List<CreateTaskTimeRage> timeRages = getDayTimeRage(currentTime, num);
                    currentWeek.setTimeRages(timeRages);
                }
            }
        }
        return filterWeeks;
    }

    /**
     * 获取计算月
     *
     * @param taskMonths
     * @param timeList
     * @param inspectionNum
     * @param firstDate
     * @return
     */
    public List<CreateTaskDays> getMonthTaskTime(List<CreateTaskDays> taskMonths, List<InsPlanExecutionTime> timeList, Integer inspectionNum, LocalDate firstDate) {
        List<CreateTaskDays> filterMonths = taskMonths.stream().filter(a -> a.getNum() > 0).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(filterMonths)) {
            int count = filterMonths.size();
            boolean bool = false;
            // 判断执行时间是否为空 是否和巡检次数相等
            if (CollUtil.isNotEmpty(timeList)) {
                bool = timeList.size() == inspectionNum ? true : false;
            }
            if (bool) {
                int num = inspectionNum / count;
                int over = inspectionNum % count;
                for (int i = 0; i < count; i++) {
                    CreateTaskDays currentMonth = filterMonths.get(i);
                    int day = currentMonth.getDay();
                    LocalDateTime monthDate = firstDate.plusDays((day - 1)).atStartOfDay();
                    List<CreateTaskTimeRage> timeRages = new ArrayList<>();
                    for (int j = 0; j < num; j++) {
                        int index = (j * count) + i;
                        InsPlanExecutionTime execution = timeList.get(index);
                        CreateTaskTimeRage timeRage = CreateTaskTimeRage.builder()
                                .startTime(monthDate.with(execution.getStartTime()))
                                .endTime(monthDate.with(execution.getEndTime()))
                                .build();
                        timeRages.add(timeRage);
                    }
                    currentMonth.setTimeRages(timeRages);
                }
                for (int i = 0; i < over; i++) {
                    if (i < filterMonths.size()) {
                        int index = (num * count) + i;
                        InsPlanExecutionTime execution = timeList.get(index);
                        CreateTaskDays overMonth = filterMonths.get(i);
                        int month = overMonth.getDay();
                        LocalDateTime monthDate = firstDate.plusDays((month - 1)).atStartOfDay();
                        CreateTaskTimeRage timeRage = CreateTaskTimeRage.builder()
                                .startTime(monthDate.with(execution.getStartTime()))
                                .endTime(monthDate.with(execution.getEndTime()))
                                .build();
                        if (overMonth.getTimeRages() != null) {
                            overMonth.getTimeRages().add(timeRage);
                        } else {
                            List<CreateTaskTimeRage> timeRages = new ArrayList<>();
                            timeRages.add(timeRage);
                            overMonth.setTimeRages(timeRages);
                        }
                    }
                }
            } else {
                for (int i = 0; i < count; i++) {
                    CreateTaskDays currentMonth = filterMonths.get(i);
                    int month = currentMonth.getDay();
                    int num = currentMonth.getNum();
                    LocalDateTime currentTime = firstDate.plusDays((month - 1)).atStartOfDay();
                    List<CreateTaskTimeRage> timeRages = getDayTimeRage(currentTime, num);
                    currentMonth.setTimeRages(timeRages);
                }
            }
        }
        return filterMonths;
    }

    /**
     * 获取每天执行时间
     *
     * @param currentTime 当天日期
     * @param num         执行次数
     * @return
     */
    public List<CreateTaskTimeRage> getDayTimeRage(LocalDateTime currentTime, Integer num) {
        LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(currentTime);
        LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(currentTime, true);
        int min = (24 * 60) / num;
        List<CreateTaskTimeRage> timeRages = new ArrayList<>();
        do {
            CreateTaskTimeRage timeRage = CreateTaskTimeRage.builder()
                    .startTime(beginOfDay)
                    .endTime(endOfDay)
                    .build();
            timeRages.add(timeRage);
            beginOfDay = beginOfDay.plusMinutes(min);
        } while (beginOfDay.isBefore(endOfDay));
        return timeRages;
    }

    /**
     * 获取每周执行次数
     *
     * @param inspectionNum
     * @param weekly
     * @return
     */
    public List<CreateTaskWeek> getWeeklyNum(Integer inspectionNum, String weekly) {
        List<String> filterWeekly = Arrays.asList(WEEKLY_DAYS.split(","));
        // 没有选和选择0都为不限制 不限制则在周一开始生成
        if (StrUtil.isNotEmpty(weekly)) {
            String[] weeklyStr = weekly.split(",");
            List<String> weeklyList = Arrays.asList(weeklyStr);
            boolean noLimit = weeklyList.contains("0");
            // 限制
            if (!noLimit) {
                filterWeekly = weeklyList.stream().filter(a -> NumberUtil.isNumber(a)).collect(Collectors.toList());
            }
        }
        return getWeekNum(inspectionNum, filterWeekly);
    }

    /**
     * 获取每月生成次数
     *
     * @param inspectionNum
     * @param monthly
     * @param localDate
     * @return
     */
    public List<CreateTaskDays> getMonthlyNum(Integer inspectionNum, String monthly, LocalDate localDate) {
        List<String> filterMonthly = getMonthlyDays(localDate);
        // 没有选和选择0都为不限制 不限制则按巡检次数间隔生成
        if (StrUtil.isNotEmpty(monthly)) {
            String[] monthlyStr = monthly.split(",");
            List<String> monthlyList = Arrays.asList(monthlyStr);
            boolean noLimit = monthlyList.contains("0");
            // 限制
            if (!noLimit) {
                filterMonthly = monthlyList.stream().filter(a -> NumberUtil.isNumber(a)).collect(Collectors.toList());
            }
        }
        return getAllDaysNum(inspectionNum,filterMonthly);
    }

    /**
     * 计算每周生成天数
     *
     * @param inspectionNum
     * @param filterWeekly
     * @return
     */
    public List<CreateTaskWeek> getWeekNum(Integer inspectionNum, List<String> filterWeekly) {
        int day = filterWeekly.size();
        if (day > 0) {
            int num = inspectionNum / day;
            int over = inspectionNum % day;
            if (CollUtil.isNotEmpty(filterWeekly)) {
                List<CreateTaskWeek> taskWeeks = new ArrayList<>();
                filterWeekly.forEach(weekDay -> {
                    CreateTaskWeek taskWeek = CreateTaskWeek.builder()
                            .week(Integer.valueOf(weekDay))
                            .num(num)
                            .build();
                    taskWeeks.add(taskWeek);
                });
                for (int i = 0; i < over; i++) {
                    if (i < taskWeeks.size()) {
                        CreateTaskWeek taskWeek = taskWeeks.get(i);
                        int newNum = taskWeek.getNum() == null ? 0 : taskWeek.getNum();
                        taskWeek.setNum(newNum + 1);
                    }
                }
                return taskWeeks;
            }
        }
        return null;
    }

    /**
     * 计算每月 每季度 每半年 每年生成次数
     *
     * @param inspectionNum
     * @param allDays
     * @return
     */
    public List<CreateTaskDays> getAllDaysNum(Integer inspectionNum, List<String> allDays) {
        int day = allDays.size();
        if (day > 0) {
            int num = inspectionNum / day;
            int over = inspectionNum % day;
            if (CollUtil.isNotEmpty(allDays)) {
                List<CreateTaskDays> taskMonths = new ArrayList<>();
                allDays.forEach(monthDay -> {
                    CreateTaskDays taskWeek = CreateTaskDays.builder()
                            .day(Integer.valueOf(monthDay))
                            .num(num)
                            .build();
                    taskMonths.add(taskWeek);
                });
                if(num>0){
                    for (int i = 0; i < over; i++) {
                        if (i < taskMonths.size()) {
                            CreateTaskDays taskMonth = taskMonths.get(i);
                            int newNum = taskMonth.getNum() == null ? 0 : taskMonth.getNum();
                            taskMonth.setNum(newNum + 1);
                        }
                    }
                }else{
                    int space = day / over;
                    for (int i = 0; i < over; i++) {
                        int index = i * space;
                        if (index < taskMonths.size()) {
                            CreateTaskDays taskMonth = taskMonths.get(index);
                            int newNum = taskMonth.getNum() == null ? 0 : taskMonth.getNum();
                            taskMonth.setNum(newNum + 1);
                        }
                    }
                }
                return taskMonths;
            }
        }
        return null;
    }

    /**
     * 获取后面不是周末日期
     *
     * @param dateTime
     * @return
     */
    public LocalDateTime lastNotWeekend(LocalDateTime dateTime) {
        while (checkWeekend(dateTime)) {
            dateTime = dateTime.plusDays(1);
        }
        return LocalDateTimeUtil.beginOfDay(dateTime);
    }

    /**
     * 获取后面不是节假日日期
     *
     * @param dateTime
     * @return
     */
    public LocalDateTime lastNotHoliday(LocalDateTime dateTime) {
        return null;
    }

    /**
     * 判断是否是周末
     */
    public boolean checkWeekend(LocalDateTime dateTime) {
        // 判断是否是周末
        return LocalDateTimeUtil.isWeekend(dateTime);
    }

    /**
     * 判断日期是否是节假日
     */
    public boolean checkHoliday(LocalDateTime dateTime) {
        // 判断是否是节假日
        return false;
    }

    /**
     * 今天是否是节假日 （数据放入缓存中）
     *
     * @return
     */
    public boolean isHoliday() {
        //todo 调用排班日历检查是否是节假日
        return false;
    }

    /**
     * 今天是否是周末
     */
    public boolean isWeekend() {
        // 判断是否是周末
        return LocalDateTimeUtil.isWeekend(LocalDateTime.now());
    }
}
